#include "uartProtocol.h"

UartProtocolCtrl_Def uartProtocolCtrl;

///////////////     payload     ////////////////////////
// 初始化UART协议
void UART_Protocol_Init(UartProtocol_Def *p)
{
    p->header[0] = UART_PROTOCOL_HEADER1;
    p->header[1] = UART_PROTOCOL_HEADER2;
    p->cmd = 0;
    p->length = 0;
    memset(p->data, 0, sizeof(p->data));
    p->checksum = 0;
}

// 计算校验和
uint8_t UART_Protocol_CalculateChecksum(UartProtocol_Def *p)
{
    uint8_t sum = 0;
    sum += p->cmd;
    sum += p->length;
    for (uint8_t i = 0; i < p->length; i++)
    {
        sum += p->data[i];
    }
    return sum;
}

///////////////     control     ////////////////////////
// 初始化
void UART_Protocol_Ctrl_Init(UartProtocolCtrl_Def *upc, UART_HandleTypeDef *huart)
{
    upc->huart = huart;
    upc->isFrameDone = 0;
    memset(upc->uartRxBuff, 0, UART_PROTOCOL_BUFFSIZE);
    RingBuff_Init(&upc->rb_rxBuff, RingBuff_OVERWRITE_OLD);
    UART_Protocol_Init(&upc->frame);
}

// 开启DMA和IDLE中断接收数据
void UART_Protocol_BeginReceive(UartProtocolCtrl_Def *upc)
{
    HAL_UART_Receive_DMA(upc->huart, upc->uartRxBuff, UART_PROTOCOL_BUFFSIZE);
    __HAL_UART_ENABLE_IT(upc->huart, UART_IT_IDLE);
}

// 写在中断里的判断和处理
void UART_Protocol_IRQHandler(UartProtocolCtrl_Def *upc)
{
    static uint16_t old_pos = 0;
    if (__HAL_UART_GET_FLAG(upc->huart, UART_FLAG_IDLE))
    {
        __HAL_UART_CLEAR_IDLEFLAG(upc->huart);
        // 计算 DMA 当前写指针
        uint16_t pos = UART_PROTOCOL_BUFFSIZE - __HAL_DMA_GET_COUNTER(upc->huart->hdmarx);

        uint16_t size;
        if (pos >= old_pos)
        {
            // 正常增长
            size = pos - old_pos;
        }
        else
        {
            // 发生了环绕
            size = (UART_PROTOCOL_BUFFSIZE - old_pos) + pos;
        }
        if (size > 0)
        {
            // 将数据放入环形缓冲区
            UART_Protocol_Ctrl_DataToRingBuff(upc, old_pos, size);
        }
        old_pos = pos; // 更新位置
        // DMA循环模式下就不需要重新启动DMA
        // 重新启动 DMA 接收
        // HAL_UART_Receive_DMA(upc->huart, upc->uartRxBuff, UART_PROTOCOL_BUFFSIZE);
    }
}
// 将接受到的数据放入环形缓冲区
void UART_Protocol_Ctrl_DataToRingBuff(UartProtocolCtrl_Def *upc, uint16_t startPos, uint16_t len)
{
    for (uint16_t i = 0; i < len; i++)
    {
        RingBuff_PushBack(&upc->rb_rxBuff, upc->uartRxBuff[startPos + i]);
    }
}
// 解析环形缓冲区数据，返回是否成功解析出一帧
// usage:放在主循环里定期调用
bool UART_Protocol_Ctrl_ParseFrame(UartProtocolCtrl_Def *upc)
{
    // 状态机变量
    static enum {
        WAIT_HEADER1,
        WAIT_HEADER2,
        WAIT_CMD,
        WAIT_LENGTH,
        WAIT_DATA,
        WAIT_CHECKSUM
    } state = WAIT_HEADER1;

    RingBuffer_Def *rb = &upc->rb_rxBuff;
    UartProtocol_Def *frame = &upc->frame;

    uint8_t data_index = 0; // 数据索引

    // 长度预判
    if (RingBuff_Count(rb) < 5)
    {
        // TODO 数据不足，至少需要5字节（头1+头2+命令+长度+校验和）
        return false;
    }

    while (RingBuff_Count(rb) > 0)
    {
        uint32_t tmp;
        uint8_t byte;
        RingBuff_PopFront(rb, &tmp); // 从环形缓冲区读取一个字节
        byte = (uint8_t)tmp;

        switch (state)
        {
        case WAIT_HEADER1:
            if (byte == UART_PROTOCOL_HEADER1)
            {
                frame->header[0] = byte;
                state = WAIT_HEADER2;
            }
            break;

        case WAIT_HEADER2:
            if (byte == UART_PROTOCOL_HEADER2)
            {
                frame->header[1] = byte;
                // 帧格式正确，继续接收后续字段
                state = WAIT_CMD;
            }
            else if (byte == UART_PROTOCOL_HEADER1)
            {
                // 连续收到两个帧头1，保持状态
                frame->header[0] = byte;
                state = WAIT_HEADER2;
            }
            else
            {
                // TODO 帧头错误，可能需要处理错误
                state = WAIT_HEADER1; // 重新等待帧头1
            }
            break;

        case WAIT_CMD:
            frame->cmd = byte;
            state = WAIT_LENGTH;
            break;

        case WAIT_LENGTH:
            frame->length = byte;
            if (frame->length > UART_PROTOCOL_BUFFSIZE)
            {
                // TODO 数据长度超出缓冲区大小，重置状态机
                state = WAIT_HEADER1;
                break;
            }
            data_index = 0;
            if (frame->length == 0)
            {
                state = WAIT_CHECKSUM; // 无数据，直接等待校验和
            }
            else
            {
                state = WAIT_DATA; // 等待数据
            }
            break;

        case WAIT_DATA:
            frame->data[data_index++] = byte;
            if (data_index >= frame->length)
            {
                state = WAIT_CHECKSUM;
            }
            break;

        case WAIT_CHECKSUM:
            frame->checksum = byte;
            // 重置状态机，准备接收下一帧
            state = WAIT_HEADER1;
            // 校验和验证
            tmp = UART_Protocol_CalculateChecksum(frame);
            if (frame->checksum == tmp)
            {
                // 成功解析一帧
                upc->isFrameDone = 1;

                return true;
            }
            else
            {
                // TODO 校验和错误，可能需要处理错误
                log_printf(LOG_MODULE_UARTPROTOCOL, "Checksum error: expected 0x%02X, got 0x%02X\n", tmp, frame->checksum);
            }
            break;
        }
    }
    return false; // 未能成功解析一帧
}

// 清除已完成标志
void UART_Protocol_Ctrl_ClearFrameDone(UartProtocolCtrl_Def *upc)
{
    upc->isFrameDone = 0;
}

// 发送
void UART_Protocol_SendFrame(UartProtocolCtrl_Def *upc, UartProtocol_Def *frame)
{
    if (upc == NULL || upc->huart == NULL || frame == NULL)
    {
        return;
    }

    uint8_t sendBuff[UART_PROTOCOL_BUFFSIZE + 5]; // 2字节头 + 1字节命令 + 1字节长度 + 数据 + 1字节校验和
    uint16_t totalLength = 0;

    // 帧头
    sendBuff[0] = UART_PROTOCOL_HEADER1;
    sendBuff[1] = UART_PROTOCOL_HEADER2;
    totalLength += 2;

    // 命令字
    sendBuff[totalLength++] = frame->cmd;

    // 数据长度
    sendBuff[totalLength++] = frame->length; // 以字节为单位

    // 数据
    if (frame->length > 0 && frame->length <= UART_PROTOCOL_BUFFSIZE)
    {
        memcpy(&sendBuff[totalLength], frame->data, frame->length);
        totalLength += frame->length;
    }

    // 计算并添加校验和
    frame->checksum = UART_Protocol_CalculateChecksum(frame);
    sendBuff[totalLength++] = frame->checksum;

    // 通过UART发送数据
    HAL_UART_Transmit(upc->huart, sendBuff, totalLength, HAL_MAX_DELAY);
}